import librosa  # 导入 librosa 库，用于音频处理（如特征提取、预加重等）
import numpy as np  # 导入 NumPy 库，用于高效数值计算
import soundfile as sf  # 导入 soundfile 库，用于加载音频文件


class AudioProcessor:  # 定义音频处理器类

    @staticmethod
    def load_audio(path, sr=16000):
        """加载音频并统一采样率"""
        try:
            y, orig_sr = sf.read(path)  # 使用 soundfile 加载音频，返回音频数据和原始采样率
            if y.ndim > 1:  # 如果音频是多声道（ndim > 1），将其转换为单声道
                y = y.mean(axis=1)
            y = librosa.resample(y, orig_sr=orig_sr, target_sr=sr)  # 将音频重采样到目标采样率
            return y, sr  # 返回重采样后的音频数据和目标采样率
        except Exception as e:  # 捕获异常（如文件损坏或路径错误）
            print(f"Error loading {path}: {str(e)}")  # 打印错误信息
            return np.zeros(sr * 1), sr  # 返回 1 秒静音作为后备方案

    @staticmethod
    def extract_features(y, sr=16000, n_mfcc=13, max_len=100):
        """完整的特征提取流程"""

        # 预加重
        y = librosa.effects.preemphasis(y)  # 对音频信号进行预加重，增强高频成分

        # VAD 语音活性检测
        trimmed, _ = librosa.effects.trim(y, top_db=20)  # 去除静音段，保留语音活性部分
        if len(trimmed) < int(0.3 * sr):  # 如果裁剪后的音频长度小于 0.3 秒
            trimmed = y  # 保留原始音频（避免过短导致无效）

        # 提取 MFCC 特征
        mfcc = librosa.feature.mfcc(
            y=trimmed,
            sr=sr,
            n_mfcc=n_mfcc,  # 提取的 MFCC 数量，默认为 13
            n_fft=int(0.025 * sr),  # FFT 窗口长度（25ms）
            hop_length=int(0.01 * sr)  # 帧移步长（10ms）
        )

        # 计算差分特征
        delta = librosa.feature.delta(mfcc)  # 一阶差分特征
        delta2 = librosa.feature.delta(mfcc, order=2)  # 二阶差分特征
        features = np.vstack([mfcc, delta, delta2])  # 将 MFCC 和差分特征堆叠在一起，形状为 (39, T)

        # 标准化
        features = (features - np.mean(features)) / (np.std(features) + 1e-8)  # 对特征进行标准化处理

        # 长度标准化
        if features.shape[1] < max_len:  # 如果特征时间步数小于最大长度
            pad_width = max_len - features.shape[1]  # 计算需要填充的宽度
            features = np.pad(features, ((0, 0), (0, pad_width)), mode='edge')  # 使用边缘填充
        elif features.shape[1] > max_len:  # 如果特征时间步数大于最大长度
            start = np.random.randint(0, features.shape[1] - max_len)  # 随机选择起始点
            features = features[:, start:start + max_len]  # 截取固定长度的特征
        # 如果特征时间步数等于 max_len，则不做处理

        return features  # 返回标准化后的特征矩阵

    @staticmethod
    def augment_audio(y, sr):
        """对音频进行数据增强"""

        # 限制增强幅度
        y = y * np.random.uniform(0.9, 1.1)  # 缩小音量变化范围（随机缩放音频幅值）

        # 添加适度的噪声
        if np.random.rand() < 0.5:  # 以 50% 的概率添加噪声
            noise = np.random.normal(0, 0.002, len(y))  # 生成高斯噪声
            y += noise  # 将噪声叠加到音频信号中

        # 控制音高变化范围
        if np.random.rand() < 0.3:  # 以 30% 的概率进行音高变换
            y = librosa.effects.pitch_shift(y, sr=sr, n_steps=np.random.randint(-1, 2))  # 随机调整音高

        return y  # 返回增强后的音频信号
    #这段代码实现了一个完整的音频处理流程，包括音频加载、特征提取和数据增强。它旨在为唤醒词检测任务提供高质量的输入数据，确保模型能够高效地学习音频特征。